/*
 * FortressManager - functions for placing and testing fortress pieces
 *
 * Copyright 2007 James W. Hofmann
 *
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

////////////todo: confirming new pieces is a part of gameplay now,
////////////but upgrades are not defined

#if flash9
import flash.Lib;
#end

import lib3f.IntPoint;

class FortressManager
{

	public static function nospaceleft() : Bool
	{
		for (p in Graph.iter(Game.cur.map))
		{
				if (test(p.x,p.y) && nearfort(p.x,p.y))
				{
						return false;
				}
		}
		return true;
	}

	public function resetadditions()
	{
		for (n in Game.cur.fortadditions)
		{
			n.kill();
		}
		
		for (n in Game.cur.fortremovals)
		{
			n.upgradeunkill();
		}
		
		Game.cur.fortadditions = new Array();		
		Game.cur.fortremovals = new Array();		
	}

	public static function towerscantbeupgraded() : Bool
	{
		for (p in Graph.iter(Game.cur.map))
		{
				if (p.fort!=null && p.fort.shooter!=null && p.fort.shooter.weapon==0)
				{
					return false;
				}
		}
		return true;
	}
	
	public static function cantfitpiece(pz : PuzzleShape) : Bool
	{
		for (n in 0...4)
		{
			for (p in Graph.iter(Game.cur.map))
			{
				if (testshape(p.x,p.y,pz))
				{
					return false;
				}
			}
			pz.rotate();
		}
		return true;
	}
	
	public static function test(tx : Int, ty: Int) : Bool
	{
		// test if square is "open" for placement/movement
		
		var gl : IntPoint = Graph.getsize(Game.cur.map);
		
		if (
			(tx<0 || tx>=gl.x || 
			ty<0 || ty>=gl.y)
			||
			!(Game.cur.map[tx][ty].terrain >= 0)
			)
		{
			return false;
		}
		
		if (Game.cur.map[tx][ty].fort!=null)
		{
			return false;
		}
		return true;
	}

	public static function test_land(tx : Int, ty: Int) : Bool
	{
		
		var gl : IntPoint = Graph.getsize(Game.cur.map);
		
		if (
			(tx<0 || tx>=gl.x || 
			ty<0 || ty>=gl.y)
			||
			!(Game.cur.map[tx][ty].terrain >= 0)
			)
		{
			return false;
		}
		
		return true;
	}
	
	public static function add(tx : Int, ty : Int, style : Int, ?ok : Bool) : FortressData
	{
		
		if (ok==null)
		{
			ok = false;
			switch (style)
			{
				default:
					ok = (test(tx,ty) && nearfort(tx,ty));
				case(3):
					ok = (test(tx,ty) && farfort(tx,ty));				
			}
		}
		var newfort : FortressData = null;
		if (ok)
		{
			newfort = new FortressData(tx, ty, style);
			Game.cur.map[tx][ty].fort = newfort;
			Creep.hurtpoint(tx,ty,9999);
			Game.cur.fortadditions.push(newfort);
		}
		return newfort;
	}

	public static function confirm()
	{
		Game.cur.fortadditions = new Array();
		
		for (n in Game.cur.fortremovals)
		{
			n.kill();
		}
		
		Game.cur.fortremovals = new Array();
	}

	public static function hurt(tx :Int, ty : Int, damage:Int)
	{
		var hit : FortressData = Game.cur.map[tx][ty].fort;
		if (hit!=null)
		{
			hit.health-=damage;
			if (hit.health<=0)
			{
				kill(tx,ty);
			}
		}
	}
	
	public static function kill(tx : Int, ty : Int)
	{
	
		var victim : FortressData = Game.cur.map[tx][ty].fort;
	
		victim.kill();
		victim = null;
		Game.cur.map[tx][ty].fort = null;
	}
	
	public static function testshape(tx : Int, ty : Int, news : PuzzleShape) : Bool
	{
		// find center
		
		var temptx : Int = tx;
		var tempty : Int = ty;
	
		for (y in 0...news.myshape.length)
		{
			for (x in 0...news.myshape[0].length)
			{	
				if (news.myshape[y][x]==2)
				{
					temptx-=x;
					tempty-=y;
				}
			}
		}
	
		// test for validity

		var nearf : Bool = false;
		
		for (y in 0...news.myshape.length)
		{
			for (x in 0...news.myshape[0].length)
			{	
				if ((news.myshape[y][x]!=0) && !(test(temptx+x,tempty+y)))
				{
					return false;
				}
				else if (!nearf && news.myshape[y][x]!=0)
				{
						nearf = nearfort(temptx+x,tempty+y);
				}
			}
		}

		if (!nearf) return false;

		return true;
	}

	public static function addshape(tx : Int, ty : Int, news : PuzzleShape) : Bool
	{

		if (!testshape(tx,ty,news)) return false;
		
		for (y in 0...news.myshape.length)
		{
			for (x in 0...news.myshape[0].length)
			{	
				if (news.myshape[y][x]==2)
				{
					tx-=x;
					ty-=y;
				}
			}
		}
		
		for (y in 0...news.myshape.length)
		{
			for (x in 0...news.myshape[0].length)
			{	
				if (news.myshape[y][x]!=0)
				{
					add(tx+x,ty+y, 1);
				}
			}			
		}	

		news.replace();		
		return true;
	}

	static public function addtower(tx : Int, ty : Int)
	{
		add(tx,ty,3);
	}

	public static function addcastle(tx : Int, ty : Int)
	{
		add(tx,ty,2, true);
	}

	public static function fencecastle(tx : Int, ty : Int)
	{
	
		// expanding strategy: we start with the castle tile
		// mark it "1" - it will expand into all the adjacent regions
		// including diagonals
		// It performs the expansion, as long as it's not going over
		// water, and then sets itself to 2 (don't touch)
		// the last set of "1"s become the walls

		// rewrite this to be dumber and just follow the brush?
		
		/*var radius : Int = 2;
		var fence : Hash2D<Int> = new Hash2D();
		fence.set(tx,ty,1);
		var currad : Int = 0;
		while (currad<=radius)
		{
			var nextlists : Array<Array<IntPoint>> = new Array();
			for (x in 0...26)
			{
				for (y in 0...26)
				{
					if (fence.exists(x,y) && fence.get(x,y)==1)
					{
						nextlists.push(iteratefence(fence,x,y));
						fence.set(x,y,2);						
					}
				}
			}
			for (l in nextlists)
			{
				for (p in l)
				{
					fence.set(Std.int(p.x),Std.int(p.y),1);
				}
			}
			currad+=1;
		}
		
		// now add the walls to the real map
		
		for (x in 0...26)
		{
			for (y in 0...26)
			{
				if (fence.exists(x,y) && fence.get(x,y)==1)
				{
					add(x,y,1);
				}
			}
		}*/		
		
		add(tx+1,ty+1,1, true);
		add(tx-1,ty+1,1, true);
		add(tx+1,ty-1,1, true);
		add(tx-1,ty-1,1, true);
		add(tx,ty-1,1, true);
		add(tx,ty+1,1, true);
		add(tx-1,ty,1, true);
		add(tx+1,ty,1, true);
	}
	
	private function iteratefence(fence, fencey, fencex) : Array<IntPoint>
	{
		var next : Array<IntPoint> = new Array();
		if (test(fencex+1,fencey+1) && !fence.exists(fencex+1,fencey+1))
			next.push(new IntPoint(fencex+1, fencey+1));
		if (test(fencex,fencey+1) && !fence.exists(fencex,fencey+1))
			next.push(new IntPoint(fencex, fencey+1));
		if (test(fencex-1,fencey+1) && !fence.exists(fencex-1,fencey+1))
			next.push(new IntPoint(fencex-1, fencey+1));
		if (test(fencex+1,fencey) && !fence.exists(fencex+1,fencey))
			next.push(new IntPoint(fencex+1, fencey));
		if (test(fencex-1,fencey) && !fence.exists(fencex-1,fencey))
			next.push(new IntPoint(fencex-1, fencey));
		if (test(fencex+1,fencey-1) && !fence.exists(fencex+1,fencey-1))
			next.push(new IntPoint(fencex+1, fencey-1));
		if (test(fencex,fencey-1) && !fence.exists(fencex,fencey-1))
			next.push(new IntPoint(fencex, fencey-1));
		if (test(fencex-1,fencey-1) && !fence.exists(fencex-1,fencey-1))
			next.push(new IntPoint(fencex-1, fencey-1));
		return next;
	}
	
	static public function calcterritory() : Int
	{
		
		var territorycount : Int = 0;
		var castlecount : Int = 0;		
		var existingcount : Int = 0;

		var cache : Array<GameTile> = new Array();
		
		for (p in Graph.iter(Game.cur.map))
		{
			cache.push(p);
		}
		
		for (p in cache)
		{
			if (p.terrain==5)
			{
				// make lost territory look "deserted"				
				p.terrain=6;
			}
			if (p.fort!=null)
			{
				if (p.fort.castlecontrol==true)
					existingcount++;
				p.fort.castlecontrol = false;
			}
		}
		
		// fence is set to "0,1,2" : 0 is unexplored area
		//							 1 is external area, emerging from water
		//							 2 is explored area
		//							 everything starts set to 0, except boundaries
		//							 then we sweep over the map
		
		// setup 1 and 0
		for (p in cache)
		{
			if (!(p.terrain>=0) || ((p.fort==null) && (p.x==0 || p.x==25 || p.y==0 || p.y==25)))
			{
				p.territory = 1;
			}
			else
			{
				p.territory = 0;
			}
		}

		var cont : Bool = true;

		// second pass : set 2
		while (cont)
		{
			cont = false;
			for (p in cache)
			{
				if (p.territory==1)
				{
					cont = true;
					p.territory=2;
					if (p.fort==null || !p.fort.iscastle) 
					{
						var x : Int = p.x;
						var y : Int = p.y;
						if (x-1>=0 && Game.cur.map[x-1][y].territory==0 &&
									  Game.cur.map[x-1][y].fort==null)
						{
							Game.cur.map[x-1][y].territory=1;
						}
						if (x+1<=25 && Game.cur.map[x+1][y].territory==0 &&
						 			   Game.cur.map[x+1][y].fort==null)
						{
							Game.cur.map[x+1][y].territory=1;
						}
						if (y-1>=0 && Game.cur.map[x][y-1].territory==0 &&
									  Game.cur.map[x][y-1].fort==null)
						{
							Game.cur.map[x][y-1].territory=1;
						}
						if (y+1<=25 && Game.cur.map[x][y+1].territory==0 &&
									   Game.cur.map[x][y+1].fort==null)
						{
							Game.cur.map[x][y+1].territory=1;
						}
					}
				}
			}
		}

		for (p in cache)
		{
			if (p.territory==0)
			{
				p.terrain=5;
				territorycount+=1;
				Creep.hurtpoint(p.x,p.y,9999);
				if (p.fort!=null && p.fort.iscastle &&
					Game.cur.map[p.x-1][p.y].territory==0 &&
					Game.cur.map[p.x+1][p.y].territory==0 &&
					Game.cur.map[p.x][p.y-1].territory==0 &&
					Game.cur.map[p.x][p.y+1].territory==0
					)
				{
					castlecount++;
					p.fort.castlecontrol = true;
				}
			}
		}		
		
		#if flash9
		if (existingcount<castlecount)
			SFX.gotcastle = true;

		FortressSprite.updateflags();

		Main.bg.build();
		#end

		return territorycount;
		
	}

	static public function calcshores()
	{
	
		// if fortress pieces are within some n steps of a shoreline
		// those shores are marked as good candidates for landings
		// this is a lazy array-based algorithm that I may replace with a general graph thingy
		// if it's too slow
	
		for (n in Graph.iter(Game.cur.map))
		{
			if (n.fort!=null)
				n.shouldlandhere = true;
			else
				n.shouldlandhere = false;
		}
		
		for (times in 0...30)
		{
			for (n in Graph.iter(Game.cur.map))
			{
				if (n.shouldlandhere==true && n.terrain>=0)
				{
					for (z in n.links)
					{
						z.shouldlandhere=true;
					}
				}
			}
		}	

		Game.cur.creepman.preferredshore = new Array();
		
		for (p in Game.cur.creepman.shore)
		{
			var next : Array<GameTile> = new Array();
			for (t in p)
			{
				if (t.shouldlandhere)
					next.push(t);
			}
			Game.cur.creepman.preferredshore.push(next);
		}
		
	}
	
	static public function nearfort(x,y) : Bool
	// piece can fit next to a fort
	{
	
		var sz : IntPoint = Graph.getsize(Game.cur.map);
		if (!Graph.inbounds(x,y,Game.cur.map)) return false;
		return ((x>0 && Game.cur.map[x-1][y].fort!=null) ||
				(x<sz.x-1 && Game.cur.map[x+1][y].fort!=null) ||
				(y>0 && Game.cur.map[x][y-1].fort!=null) ||
				(y<sz.y-1 && Game.cur.map[x][y+1].fort!=null)
				);
	}

	static public function farfort(x,y) : Bool
	// nearfort with a larger radius
	{
		return (nearfort(x,y) || nearfort(x-1,y) || nearfort(x+1,y)
				|| nearfort(x,y-1) || nearfort(x,y+1));
	}
	
}
